package computerPlayer;

import java.awt.Color;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import miniMax.Node;
import miniMax.TicTacToeFramework;



public class MinMaxPlayer extends Player {

	private TicTacToeFramework game;
	@SuppressWarnings("unused")
	private static Map<TicTacToeFramework, Integer> possGameSituations;


	private Set<Node> nodeSet = new LinkedHashSet<Node>();
	private Node rootNode;
	private int minInf = Integer.MIN_VALUE/10000;
	private int maxInf = Integer.MAX_VALUE/10000;;
	private final static String playerChar = "M"; 
	private final static String playerName = "Min-Max"; 


	public MinMaxPlayer(TicTacToeFramework ticTacToe, Player otherPlayer) {
		super(playerChar, playerName);
		super.setColor(Color.CYAN);
		super.setOtherPlayer(otherPlayer);
		this.game = ticTacToe;
		possGameSituations = new HashMap<TicTacToeFramework, Integer>();
	}
	
	/**
	 * Dont forget to set other player
	 * @param ticTacToe
	 */
	public MinMaxPlayer(TicTacToeFramework ticTacToe) {
		super(playerChar, playerName);
		super.setColor(Color.CYAN);
		this.game = ticTacToe;
		possGameSituations = new HashMap<TicTacToeFramework, Integer>();
	}

	public final int makeMove() {
		List<Integer> moves = game.possFields();
		long start = System.currentTimeMillis();
		if (moves.size() > 0) {
			minMax();
			long end = System.currentTimeMillis();
			System.out.println("Dauer in Milli-Sek: " + (end - start));
			setValuesOfNodes(rootNode);
			int move = selectMove();
			System.out.println("Make MOVE " + move + " FOR ME..I WILL WIN!");
			return move;
		} else
			return -1;
	}

	@SuppressWarnings("unused")
	private void print(Node root, int ebene) {
		List<Node> children = root.returnChildren();
		System.out.println("-------------------");
		System.out.println("Ebene: " + ebene);
		System.out.println("Bewertung: " + root.getValue());
		root.getTtt().print();
		System.out.println("-------------------");
		for (Node node : children) {
			print(node, (ebene + 1));
		}
	}

	private int selectMove() {
		TicTacToeFramework tmp = game;
		Node actualRoot = null;
		for (Node node : nodeSet) {
			if (node.getTtt().equals(tmp)) {
				actualRoot = node;
				break;
			}
		}
		/* PC needs max */
		int min = minInf;
		Node choose = null;
		List<Node> children = actualRoot.returnChildren();
		if(!children.isEmpty()){
			min = children.get(0).getValue();
		}
		for (Node node : children) {
			if (node.getValue() >= min) {
				choose = node;
				min = node.getValue();
			}
		}
		if (choose == null) {
			System.out.println("All moves are .... bad");
			return returnRandom(actualRoot.getTtt().possFields());
		}
		return choose.getId();
	}

	private void setValuesOfNodes(Node root) {
		// Assert.assertEquals(5, nodeValueMap.size());
		List<Node> children = root.returnChildren();
		int sum = 0;
		for (Node node : children) {
			sum += node.getValue();
		}
		rootNode.setValue(sum);
		// print(rootNode, 1);
	}


	private int returnRandom(List<Integer> moves) {
		Random r = new Random();
		int next = r.nextInt(moves.size());
		return moves.get(next);
	}

	

	/* Startmethod for the min-max algo */
	private void minMax() {
		TicTacToeFramework gameTmp = new TicTacToeFramework();
		gameTmp.setGameField(game.getGameField());
		Node root = new Node(gameTmp.getGameField());
		this.rootNode = root;
		putMap(root);
		maxFunktion(root);
		System.out.println("Done");
	}


	private int maxFunktion(final Node node) {
		/* Move of Computer, needs max of the childs */
		Node subRoot = node;
		List<Integer> nextMoves = subRoot.getTtt().possFields();
		int value = maxInf;
		for (Integer posMove : nextMoves) {
			subRoot.setValue(value);
			Node child = new Node(subRoot, posMove, "Max");
			subRoot.addChild(child);
			child.getTtt().setCharWithID(posMove, MinMaxPlayer.playerChar ,false);
			if (nodeSet.contains(child)) {
				Iterator<Node> nodes = nodeSet.iterator();
				while (nodes.hasNext()) {
					Node tmp = nodes.next();
					if (tmp.equals(child)) {
						child = tmp;
						return child.getValue();
					}
				}
			}
			/* Won with this move? */
			int win = child.getTtt().checkWin(posMove);
			if (win == 1) {
				child.setValue(Integer.MAX_VALUE);
				return maxInf;
			} else if (win == 0) {
				child.setValue(win);
				return 0;
			} else {
				int tmp = minFunktion(child);
				child.setValue(tmp);				
				//if(tmp > 0 ) {
					value += tmp;
				//}
				subRoot.getTtt().removechar(posMove);
			}
		}
		
		/* Set the min-value of all childs to parent*/
		List<Node> children = subRoot.returnChildren();
		int tmp = Integer.MIN_VALUE;
		for (Node node2 : children) {
			if(node2.getValue() > tmp){
				tmp = node2.getValue();
			}
		}
		subRoot.setValue(tmp);
		return tmp;
	}

	private boolean putMap(Node node) {
		boolean contain = nodeSet.add((Node) node);
		return !contain;
	}

	private int minFunktion(final Node node) {
		/* Move of human */
		Node subRoot = node;
		List<Integer> nextMoves = subRoot.getTtt().possFields();
		for (Integer posMove : nextMoves) {
			Node child = new Node(subRoot, posMove, "Min");
			subRoot.addChild(child);
			child.getTtt().setCharWithID(posMove, super.getOtherPlayer().getPlayerChar() ,false);
			if (nodeSet.contains(child)) {
				Iterator<Node> nodes = nodeSet.iterator();
				while (nodes.hasNext()) {
					Node tmp = nodes.next();
					if (tmp.equals(child)) {
						child = tmp;
						return child.getValue();
					}
				}
			}
			/* Won with this move? */
			int win = child.getTtt().checkWin(posMove);
			if (win == 1) {
				child.setValue(Integer.MIN_VALUE);
				return Integer.MIN_VALUE;
			} else if (win == 0) {
				child.setValue(win);
				return 0;
			} else {
				int tmp = maxFunktion(child);
				child.setValue(tmp);
				subRoot.getTtt().removechar(posMove);
			}
		}
		/* Choose the HIGHEST Childs and remove all others. 
		 * The best move is a child with value <= minInf! 
		 * */
		List<Node> children = subRoot.returnChildren();
		int tmp = Integer.MAX_VALUE;
		for (Node node2 : children) {
				if(tmp > node2.getValue()){
					tmp = node2.getValue();
				}
		}
		subRoot.setValue(tmp);
		return tmp;
	}

	public Integer call() throws Exception {
		return null;
	}

	@Override
	public void reset(TicTacToeFramework game) {
		nodeSet = new LinkedHashSet<Node>();
		this.game = game;
	}

	public void run() {
	}

}
